#include "coder.h"
#include <limits.h>
#include "/usr/include/math.h"
#include "auxiliar.c"
#include "encodings.c"
#include "decodings.c"


#define GOLDEN 0.618

//TODO doxy
void context(PGMData * image, int i, int j, int * context_abcd)
{
  //TODO Check if  [0][0] is actually the top left corner.
  if((i<0)||(i==image->row)||(j<0)||(j==image->col))
    {
      fprintf(stderr, "Max gray overflow (max>255)!\n");
      exit(EXIT_FAILURE);
    }
  // Set a
  if(j==0)
    context_abcd[0]=0;
  else
    context_abcd[0]=image->matrix[i][j-1];
  // Set b
  if(i==0)
    context_abcd[1]=0;
  else
    context_abcd[1]=image->matrix[i-1][j];
  // Set c
  if((j==0)||(i==0))
    context_abcd[2]=0;
  else
    context_abcd[2]=image->matrix[i-1][j-1];
  // Set d
  if((i==0)||(j==(image->col-1)))
    context_abcd[3]=0;
  else
    context_abcd[3]=image->matrix[i-1][j+1];
}

int predictor(int * context_abcd)
{
  // Note: context[3] is not used in order to maintain simetry...
  int max,min,retval;
  if(context_abcd[0]>context_abcd[1])
    {
      max = context_abcd[0];
      min = context_abcd[1];
    }
  else
    {
      max = context_abcd[1];
      min = context_abcd[0];
    }

  if(context_abcd[2]>=max)
    retval = min;
  else 
    if (context_abcd[2] <= min)
      retval = max;
    else
      retval = (context_abcd[0] + context_abcd[1] - context_abcd[2]);
  return retval;
}

//TODO doxy
int pred_error(PGMData * image, int i, int j, int prediction)
{
  //TODO double precision for error...?
  int err = image->matrix[i][j] - prediction;
  err = fold_error(err);
  return err;
}


/** 
 * Analyzes the current pixel, determining if a new run should start, a current
 * run should end, or if the pixel value should be encoded.
 * 
 * @param context_abcd : Context of the current pixel.
 * @param error : Error pixel value Vs.  prediction.
 * @param run_count : Length of the current run (0==no run).
 * 
 * @return 0 if normal, 1 if run mode.
 *
 */
int mode(int * context_abcd, int error)
{
  int current_mode;
  if( (error==0) &&				\
      (context_abcd[0]==context_abcd[1])&&	\
      (context_abcd[1]==context_abcd[2])&&	\
      (context_abcd[2]==context_abcd[3]))
    current_mode = 1;
  else
    current_mode = 0;
  return current_mode;
}

//TODO doxy  
void end_run(int * run_count, int * running, int * mrun, int * n0, int * n1, int * Nrun, FILE * file)
{
  
  while(*run_count>=*mrun){
      egk(*mrun, *mrun, file);
      *run_count-=*mrun;
      *mrun= adapt_golomb_run(n0,n1,*mrun, *mrun,*Nrun);
      //printf("%d",*mrun);
  }
  //if (!(*run_count==0)){
    egk(*run_count,*mrun,file);
    *mrun= adapt_golomb_run(n0,n1,*run_count,*mrun,*Nrun);
  //}
  *run_count = 0;
  *running = 0;
}


//TODO doxy
int encode(PGMData * image, int N, char * filename)
{
  int A_max = 0;//TODO remove
  int min_err = 500;//TODO remove
  FILE * err_file = fopen("err_m.txt","w");//TODO remove

  int A=8; //Acum. de errores
  int n=1; //Acum. de muestras
  int m=INITIAL_GOLOMB; //Golomb param. (G_{m})
  int Nrun=N;
  int mrun=INITIAL_GOLOMB;
  bit_count = 0;// global var
  FILE * file = fopen(filename,"wb");
  if (file == NULL) {
    perror("Cannot open file to write");
    exit(EXIT_FAILURE);
  }
  header_write(image,N,file);
  int retval = ERROR_OK;
  int context_abcd[4];
  int pred_pixel, err, run_count = 0, running = 0, run = 0;
  int i,j;
  int n0=8;
  int n1=1;
  for(i=0;i<image->row;i++)
    for(j=0;j<image->col;j++)
      {
        //Calculo contexto
	context(image,i,j,context_abcd);

        //Predigo proximo pixel
	pred_pixel = predictor(context_abcd);

        if (pred_pixel<0)
            printf("%d",pred_pixel);


	err = pred_error(image,i,j,pred_pixel);
	fprintf(err_file,"%d\t%d\n",err,m);//TODO remove
	run = mode(context_abcd,err);

	if(run_count==MAX_RUNS)
	  {
	    end_run(&run_count,&running,&mrun,&n0,&n1,&Nrun,file);
	    printf("%s: MAX_RUNS reached.",__func__);
	  }

        if (running){ //Estaba en corrida
            if (run) //La corrida sigue
                run_count++;
            else{//Termina aca, escribo y codifico nuevo pixel
                end_run(&run_count,&running,&mrun,&n0,&n1,&Nrun,file);
                golomb(err,m,file);
                m=adapt_golomb(&A,&n,err,N);
            }

        }else{ //No estoy en corrida, escribo pixel actual
            if (m<128){ //Encoding normal
                golomb(err,m,file);
            
            }else{ //Encoding alternativo para errores grandes
                //code_alt(err,file);
                //plain_code(unfold_error(err)+pred_pixel,file);
                golomb(err,m,file);
            }
            //Actualizo estadistica
            m=adapt_golomb(&A,&n,err,N);
            if (run){//Comienza corrida: inicializo
                running = 1;
                run_count = 0;
            }
        }
      }
  //Finalizo archivo
  dec_to_bin_print_bin(0,0,1,file);
  retval = fclose(file);
  if (retval != ERROR_OK) {
    perror("Warning: Could not close compressed file");
  }
  fclose(err_file);//TODO remove
  return bit_count;
}


//TODO doxy
void decode(PGMData * image, char * filename)
{
  byte_buff = 0;
  byte_buff_filled = 0;
  FILE * file = fopen(filename,"rb");
  int A=8; //Acum. de errores
  int n=1; //Acum. de muestras
  int m=INITIAL_GOLOMB;//Golomb param.
  int n0=8;
  int n1=1;
  if (file  == NULL) {
    perror("cannot read compressed file");
    exit(EXIT_FAILURE);
  }
  int N;
  header_read(image,&N,file);
  int Nrun=N;
  int mrun=INITIAL_GOLOMB;
  image->matrix = allocate_dynamic_matrix(image->row, image->col);
  // decode to rebuild image
  int runs = 0, value = 0, pix_val = 0, get_run_count = 0, run = 0, i, j;
  int context_abcd[4];
  int pred_pixel, err;

  for(i=0;i<image->row;i++)
    for(j=0;j<image->col;j++)
      {
        //Leo los datos y escribo los pixeles de a uno.

	if(runs>0) //Caso en que estoy en run
	    runs--;
        else//No estoy en run ni en panic
	  {
	    context(image,i,j,context_abcd); //Calculo contexto
	    pred_pixel = predictor(context_abcd); //Predigo proximo pixel

            //Segun el caso, leo como corresponda el proximo pixel
            if (m<128) {//Caso 1: Normal, leo Golomb
                decode_current(&value,m,file);
            }
            else{//Caso 2: Panic, leo de alguna manera y calculo 'value'
                //decode_alt(&value);
                decode_current(&value,m,file);
                //plain_decode(&value,file);
                //value=fold_error(value-pred_pixel);
            }
            //Con el valor,  actualizo la estadistica
	    m=adapt_golomb(&A,&n,value,N);
            //Calculo si tengo que entrar en run en el proximo paso
            run = mode(context_abcd,value);

            //Calculo el valor a escribir
            err = unfold_error(value);
	    pix_val = pred_pixel + err;

            //Si toca entrar en run, seteo el contador de 'runs' para escribir multiples pixeles sucesivos
	    if(run)
	      {
                int check=1;
                runs=0;
                while(check==1){
                    decode_egk(&value,mrun,file);
                    if(!(value==mrun))
                        check=0;
                    mrun= adapt_golomb_run(&n0,&n1,value,mrun,Nrun);
                    runs+= value;
                }
	      }
	  }
        //En cualquier circunstancia, escribo un pixel:
	image->matrix[i][j] = pix_val;
      }
  fclose(file);
}

